////////////////////////////////////////////////////////////////////////////////
// Copyright 2010 Michael Schmalle - Teoti Graphix, LLC
// 
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
// 
// http://www.apache.org/licenses/LICENSE-2.0 
// 
// Unless required by applicable law or agreed to in writing, software 
// distributed under the License is distributed on an "AS IS" BASIS, 
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and 
// limitations under the License
// 
// Author: Michael Schmalle, Principal Architect
// mschmalle at teotigraphix dot com
////////////////////////////////////////////////////////////////////////////////

package org.teotigraphix.swizplugin.registry
{
import mx.rpc.IResponder;

import org.teotigraphix.swizplugin.core.ExecutableRequest;

/**
 * The <code>IExtensionDescriptor</code> interface marks an instance as
 * either a child of an <code>IExtension</code> or another
 * <code>IExtensionDescriptor</code>.
 * 
 * <p>The <code>IExtensionDescriptor</code> is a very important part of the
 * application extension registry implementation. From this inteface the developer
 * is able to reflect all information the was declared in MXML or XML
 * <code>IPluginExtension</code>.</p>
 * 
 * <p>The developer is also able to create executable extensions (create
 * class instances) through this interface. Depending on how the class
 * loading proccess is setup, the descriptor could create a loaded class
 * that is already present in the <code>ApplicationDomain</code> or, if a 
 * bundle service is implemented, the descriptor could delegate class loading 
 * to the bundle loader's implementation. Either way the 
 * <code>createExecutableExtension()</code> method will return
 * an <code>ExecutionRequest</code> token that creates an aynchronous link 
 * from the calling code. This allows the client of the descriptor to 
 * listen for the class load whenever and however that occurs. This creates 
 * a complete decoupled class loading experience whatever the implementation.</p>
 * 
 * <p>Another important part of this interface is the <code>options</code>
 * property and what it symbolizes. This value is the actual 
 * <code>PluginOptions</code> instance or XML markup declared in MXML. This
 * interface acts as a decorator to that instance and allows access to
 * it's property values and children. If the MXML tag or XML had children,
 * they would have actually been converted to child 
 * <code>IExtensionDescriptor</code> instances. To access these children,
 * the <code>getChild()</code> and <code>getChildren()</code> methods
 * are available. To access text node content, see <code>getValue()</code>.</p>
 * 
 * <p>It's not recommended to use the <code>options</code> property directly
 * since the developer does not know if XML or PluginOptions were declared.
 * This property might be removed in future versions of the platform. 
 * Since access to this property is discourage, the <code>getAttribute()</code>
 * method is available and will return a value whether the underlying
 * instance is XML or a PluginOptions instance.</p>
 * 
 * <p><code>PluginOptions</code> implementation in the <code>IExtension</code>;</p>
 * 
 * <pre>
 * ...
 * 
 * &lt;swizplugin:PluginExtension 
 * 	uid="org.example.acme.splash.startupSplash"
 * 	point="org.example.acme.splash"&gt;
 *	
 * 	&lt;options:SplashOptions 
 * 		uid="deployedSplash"
 * 		title="Deployed Splash"
 * 		icon="@Embed(source='/assets/logo.png')"
 * 		width="150" height="150"
 * 		duration="2000"
 * 		generator="com.acme.components.AcmeSplash"/&gt;
 * 
 * 	&lt;options:SplashOptions 
 * 		uid="debuggerSplash"
 * 		title="Debug Splash"
 * 		icon="@Embed(source='/assets/logo.png')"
 * 		width="200" height="200"
 * 		duration="5000"
 * 		generator="com.acme.components.DebugAcmeSplash"&gt;
 * 
 * 	&lt;options:PresentationOptions
 * 		effect="Fade"
 * 		alphaTo="0.2"/&gt;
 * 
 * 	&lt;/options:SplashOptions&gt;
 *     
 * &lt;/swizplugin:PluginExtension&gt;
 * 
 * ...
 * </pre>
 * 
 * <p><code>XML</code> implementation in the <code>IExtension</code>;</p>
 * 
 * <pre>
 * ...
 * 
 * &lt;swizplugin:XMLPluginExtension 
 * 	point="org.example.acme.splash"&gt;
 *     
 * 	&lt;fx:XMLList&gt;
 * 		&lt;splash id="acmeSplashOne"
 * 			title="Hello Splash One!"
 * 			icon="/assets/logo.png"
 * 			width="150" height="150"
 * 			duration="2000"
 * 			generator="org.example.acme.components.AcmeSplash"/&gt;
 * 	&lt;/fx:XMLList&gt;
 * 
 * &lt;/swizplugin:XMLPluginExtension&gt;
 * 
 * ...
 * </pre>
 * 
 * <p><strong>Notice:</strong> In the <code>XML</code> version, the 
 * <code>icon</code> and <code>generator</code> are strings and need to be 
 * resolved through some type of ImageRegistry and ClassLoader. With the
 * <code>SplashOptions</code> instance MXML implementation, the icon is embeded 
 * and the class is linked because the <code>generator</code> of the 
 * <code>SplashOptions</code> is of type <code>Class</code>.</p>
 * 
 * @author Michael Schmalle
 * @copyright Teoti Graphix, LLC
 * @productversion 1.0
 */
public interface IExtensionDescriptor
{
	//--------------------------------------------------------------------------
	//
	//  Properties
	//
	//--------------------------------------------------------------------------
	
	//----------------------------------
	//  contributor
	//----------------------------------
	
	/**
	 * Returns the <code>IBundle</code>'s <code>IContributor</code> instance.
	 */
	function get contributor():IContributor;
	
	//----------------------------------
	//  name
	//----------------------------------
	
	/**
	 * Returns the tag name of this descriptor.
	 * 
	 * <p>In MXML the property names the child node. This property must be set in
	 * order to retrieve the descriptor at a later time. In an XML implementation
	 * this is simply the name of the XML node.</p>
	 * 
	 * <p>A lot of the times there will be custom MXML implementations of a child
	 * extension node, such as SplashOptions, where the name is already set for 
	 * the developer to 'splash'. The developer then uses the options in an 
	 * extension node and does not have to worry about the name.</p>
	 */
	function get name():String;
	
	/**
	 * @private
	 */
	//function set name(value:String):void;
	
	//----------------------------------
	//  namespaceID
	//----------------------------------
	
	/**
	 * The <code>namespaceID</code> of the parenting <code>IExtension</code>.
	 */
	function get namespaceID():String;
	
	//----------------------------------
	//  uniqueID
	//----------------------------------
	
	/**
	 * The <code>uniqueID</code> of the parenting <code>IExtension</code>.
	 */
	function get uniqueID():String;
	
	//----------------------------------
	//  extension
	//----------------------------------
	
	/**
	 * The parenting <code>IExtension</code>.
	 */
	function get extension():IExtension;
	
	//----------------------------------
	//  parent
	//----------------------------------
	
	/**
	 * The parent <code>IExtension</code> or <code>IExtensionDescriptor</code>.
	 */
	function get parent():Object;
	
	//----------------------------------
	//  uid
	//----------------------------------
	
	/**
	 * The String identifier of the <code>PluginOptions</code> or XML.
	 */
	function get uid():String;
	
	//----------------------------------
	//  options
	//----------------------------------
	
	/**
	 * The <code>PluginOptions</code> or XML that was decorated by this
	 * descriptor.
	 */
	function get options():Object;
	
	//----------------------------------
	//  internalID
	//----------------------------------
	
	/**
	 * The internal registry id.
	 */
	function get internalID():int;
	
	//--------------------------------------------------------------------------
	//
	//  Methods
	//
	//--------------------------------------------------------------------------
	
	/**
	 * Starts an execution request to load and create a class by the
	 * attribute name of <code>propertyName</code>.
	 * 
	 * <p>The class loading mechanism is dependent on the platform's 
	 * implementation. The default implementation of the platform is to load
	 * classes from the same <code>ApplicationDomain</code>.
	 * 
	 * <pre>
	 * // get the extension by "uid"
	 * var extension:IExtension = extensionRegistry.
	 * 	getExtension("com.acme.splash.startupSplash");
	 * 
	 * // get the first "splash" tag child
	 * var descriptor:IExtensionDescriptor = extension.getChild("splash");
	 * 
	 * // create an execution request
	 * var request:ExecutableRequest = descriptor.createExecutableExtension("generator");
	 * 
	 * // add a result listener when the instance has been created
	 * request.addResultListener(executableReadyHandler);
	 * 
	 * private function executableReadyHandler(request:ExecutableRequest):void
	 * {
	 * 	trace(request.instance); // AcmeSplash44
	 * }
	 * </pre>
	 * 
	 * @param propertyName A String indicating the attribute value of the 
	 * underlying options instance to use as the class name of the created
	 * instance.
	 * 
	 * @param resultHandler A Function that will be called when the extension has 
	 * been loaded and instantiated.
	 * @return An <code>ExecutableRequest</code>.
	 */
	function createExecutableExtension(propertyName:String,
									   responder:IResponder = null):ExecutableRequest;
	
	/**
	 * Returns the value of <code>propertyName</code> on the underlying 
	 * <code>options</code> instance.
	 * 
	 * <pre>
	 * // this code is in a ExecutableRequest result handler
	 * // the ISplash instance
	 * var splash:ISplash = request.instance as ISplash;
	 * var descriptor:IExtensionDescriptor = request.descriptor;
	 * 		
	 * // set attributes from descriptor
	 * splash.width = descriptor.getAttribute("width");
	 * splash.height = descriptor.getAttribute("height");
	 * 
	 * var icon:Class = descriptor.getAttribute("icon") as Class;
	 * if (icon == null)
	 * {
	 * 	// this means it's a String from XML, load from image registry
	 * 	icon = imageRegistry.getImage(icon);
	 * }
	 * 
	 * splash.icon = icon;
	 * </pre>
	 * 
	 * @param propertyName A String indicating the name of the property to
	 * retrieve it's value.
	 * @return A mixed value or undefined.
	 */
	function getAttribute(propertyName:String):*;
	
	/**
	 * Returns all the property names of the underlying <code>options</code> 
	 * instance.
	 * 
	 * <pre> 
	 * // get the extension by "uid"
	 * var extension:IExtension = extensionRegistry.
	 * 	getExtension("com.acme.splash.startupSplash");
	 * 
	 * // get the first "splash" tag child
	 * var descriptor:IExtensionDescriptor = extension.getChild("splash");
	 * 
	 * // loop through all names defined in PluginOptions.listEnumerations()
	 * // this could also loop through XML attributes but, "splash" is SplashOptions
	 * for each (var name:String in descriptor.getAttributeNames())
	 * {
	 * 	trace("name : " + name);
	 * 	trace("value : " + descriptor.getAttribute(name));
	 * }
	 * </pre>
	 * 
	 * @return An Array of String property names or <code>null</code>.
	 */
	function getAttributeNames():Array;
	
	/**
	 * Returns whether this descriptor has the attribute named 
	 * <code>propertyName</code>.
	 * 
	 * @param propertyName A String indicating the name of the property to
	 * retrieve it's value.
	 * @return A Boolean indicating whether the attribute exists and is not 
	 * <code>null</code>, <code>NaN</code> or <code>undefined</code>.
	 */
	function hasAttribute(propertyName:String):Boolean;
	
	/**
	 * Returns the first child <code>IExtensionDescriptor</code> by tag name.
	 * 
	 * <p><strong>Note:</strong> A PluginOptions instance must implement the
	 * <code>name</code> property either in it's subclass or in the MXML
	 * declaration. This is the name this method uses in it's search for a
	 * child.</p>
	 * 
	 * <p>See <code>IExtensionDescriptor</code> comment for PluginExtension, 
	 * XMLPluginExtension declaration;</p>
	 * 
	 * <pre>
	 * 
	 * &lt;!-- MyPlugin.mxml --&gt;
	 * &lt;plugin:XMLPluginExtension 
	 * 	uid="com.acme.tools.outlineExtension"
	 * 	point="com.acme.tools"&gt;
	 * 				
	 * 	&lt;fx:XMLList&gt;
	 * 			
	 * 		&lt;menuContribution
	 * 			locationURI="popup:com.acme.tools.showInMenu"&gt;
	 * 
	 * 			&lt;command
	 * 				commandId="com.acme.outlines.NewOutlineCommand"
	 * 				icon="platform:/icons/icon.gif"
	 * 				label="Outline"
	 * 				style="push"&gt;
	 * 
	 *  		&lt;/command&gt;
	 * 			
	 * 		&lt;/menuContribution&gt;
	 * 						
	 * 	&lt;/fx:XMLList&gt;
	 * 	
	 * &lt;/plugin:XMLPluginExtension&gt;
	 * 
	 * ...
	 * 
	 * var extension:IExtension = extensionRegistry.
	 *	getExtension("outlineExtension", "com.acme.tools");
	 * 
	 * // first child of the extension
	 * var menuContribution:IExtensionDescriptor = extension.getChild("menuContribution");
	 * trace(menuContribution.getAttribute("locationURI")); // popup:com.acme.tools.showInMenu
	 * 
	 * // first child of the menuContribution descriptor
	 * var command:IExtensionDescriptor = menuContribution.getChild("command");
	 * 
	 * // output attribute values
	 * trace(command.getAttribute("commandId")); // com.acme.outlines.NewOutlineCommand
	 * trace(command.getAttribute("label")); // Outline
	 * trace(command.getAttribute("style")); // push
	 * </pre>
	 * 
	 * @param tagName A String indicating the first tag child to return.
	 * @return The first <code>IExtensionDescriptor</code> by tagName or
	 * <code>null</code>.
	 */
	function getChild(tagName:String):IExtensionDescriptor;
	
	/**
	 * Returns all <code>IExtensionDescriptor</code> children of this 
	 * descriptor if the the tagName is null, otherwise all the children
	 * by the name tagName.
	 * 
	 * <pre>
	 * ...
	 * &lt;!-- A menu tree to be loaded as an extension --&gt;
	 * &lt;swizplugin:XMLPluginExtension 
	 * 	uid="org.example.acme.menu.customMenu"
	 * 	point="org.example.acme.menu"&gt;
	 *     
	 * 	&lt;fx:XMLList&gt;
	 * 		&lt;menu
	 * 			label="Menu One"
	 * 			icon="/assets/folder.png"/&gt;
	 * 		&lt;menu
	 * 			label="Menu Two"
	 * 			icon="/assets/folder.png"&gt;
	 * 		&lt;item
	 * 			label="Menu Item One"
	 * 			icon="/assets/page.png"/&gt;
	 * 		&lt;item
	 * 			label="Menu Item Two"
	 * 			icon="/assets/page.png"/&gt;	 * 
	 * 		&lt;/menu&gt;
	 * 		&lt;menu
	 * 			label="Menu Three"
	 * 			icon="/assets/folder.png"/&gt;
	 * 
	 *	&lt;/fx:XMLList&gt;
	 * 
	 * &lt;/swizplugin:XMLPluginExtension&gt;
	 * 
	 * ...
	 * 
	 * // actionscript loading of the menu tree
	 * var point:IExtensionPoint = extensionRegistry.
	 * 	getExtensionPoint("com.acme.menu");
	 * // load the "customMenu" in the "com.acme.menu" extension point namespace
	 * var extension:IExtension = point.getExtension("customMenu");
	 * // get the three menus from the extension
	 * var menus:Array = extension.getExtensionDescriptors();
	 * // get the two menu items from menu2
	 * var menu2:IExtensionDescriptor = menus[1];
	 * var items:Array = menu2.getChildren("item");
	 * trace(items.length); // 2
	 * trace(items[0].getAttribute("label")); // Menu Item One
	 * trace(items[1].getAttribute("label")); // Menu Item Two
	 * </pre>
	 * 
	 * 
	 * @param tagName A String indicating the tag children to return
	 * or <code>null</code> to return all children of this descriptor.
	 * @return An Array of <code>IExtensionDescriptor</code> children
	 * or <code>null</code> if no children are found.
	 */
	function getChildren(tagName:String = null):Array;
	
	/**
	 * Returns the text value of this descriptor.
	 * 
	 * <p>The text value would be a node of text in XML or if the PluginOption
	 * contains the attribute <code>text</code>, that value will be 
	 * returned.</p>
	 * 
	 * <pre>
	 * ...
	 * 
	 * &lt;swizplugin:PluginExtension 
	 * 	uid="com.acme.help.importantHelp"
	 * 	point="com.acme.help"&gt;
	 *     
	 * 	&lt;options:DocumentOptions id="acmeDocOne"&gt;
	 * 		&lt;options:text&gt;
	 * 			Some document text to load.
	 * 		&lt;/options:text&gt;
	 * 	&lt;/options:DocumentOptions&gt;
	 * 
	 * &lt;/swizplugin:PluginExtension&gt;
	 * 
	 * ...
	 * 
	 * var point:IExtensionPoint = extensionRegistry.
	 * 	getExtensionPoint("com.acme.help");
	 * 
	 * // load the "importantHelp"
	 * var extension:IExtension = point.getExtension("importantHelp");
	 * var descriptor:IExtensionDescriptor = extension.getChild("document");
	 * var text:String = descriptor.getValue();
	 * trace(text); // Some document text to load.
	 * 
	 * </pre>
	 * 
	 * @return A String of text.
	 */
	function getValue():String;
}
}